#include <phpcpp.h>
#include <iostream>
#include <map>
#include <string>
#include "MainServer.hpp"
#include "Client.hpp"
#include "Reactor.hpp"
#include <map>
#include <vector>

using namespace std;

class Server : public Php::Base {
public:
    Server() = default;
    ~Server() = default;

    void __construct(Php::Parameters &args) {
        if (args.size() < 2) throw new Php::Exception("构造函数参数错误");
        _ip = args[0].buffer();
        _port = args[1].numericValue();
        _server = new MainServer;
    }

    void start() {
        _server->createServer(_ip, _port);
        _server->Start(_reactorNum);
        RegisterEvents();
        char cmd[] = {0};
        while (true) {
            scanf("%s", cmd);
            if (strcmp("quit", cmd) == 0) {
                _server->Exit();
                break;
            }
        }
        delete _server;
        _clients.clear();
        cout << "main thread quit" << endl;
    }

    Php::Value send(Php::Parameters &args) {
        if (args.size() < 2) throw Php::Exception("Send方法参数错误");
        int fd = args[0].numericValue();
        string data = args[1].stringValue();
        if (_clients.count(fd)) {
            Client* c = _clients[fd];
            return c->PushData(data.c_str(), (int)data.length());
        }
        return false;
    }

    void close(Php::Parameters &args) {
        if (args.size() < 1) throw Php::Exception("Close方法参数错误");
        int fd = args[0].numericValue();
        if (_clients.count(fd)) {
            Reactor* r = FindReactor(fd);
            r->RemoveClient(_clients[fd]);
            _clients.erase(fd);
        }
    }

    void on(Php::Parameters &args) {
        if (args.size() < 2) throw new Php::Exception("On函数参错误");
        _events[args[0]] = args[1];
    }

    void set(Php::Parameters &args) {
        std::map<std::string , Php::Value> _s = args[0];
        for (auto &itr : _s) {
            if (itr.first == "reactorNum") {
                _reactorNum = itr.second.numericValue();
            }
        }
    }

private:
    void RegisterEvents() {
        _server->On("connect", [this](Client* c) {
            _clients[c->Sockfd()] = c;
            _events["connect"](this, c->Sockfd());
        });
        _server->On("close", [this](Client* c) {
            if (_clients.count(c->Sockfd())) {
                _clients.erase(c->Sockfd());
                _events["close"](this, c->Sockfd());
            }
        });
        _server->On([this](Client* c, const char* data, int reactorId) {
            _events["receive"](this, c->Sockfd(), reactorId, data);
        });
    }

    Reactor* FindReactor(int sockfd) {
        std::vector<Reactor*> reactors = _server->getReactors();
        Reactor* r = nullptr;
        for (auto &reactor : reactors) {
            if (reactor->findClient(sockfd)) {
                r = reactor;
                break;
            }
        }
        return r;
    }

private:
    const char* _ip;
    int _port;
    MainServer* _server;
    std::map<string, Php::Value> _events;
    std::map<int, Client*> _clients;
    int _reactorNum {1};
};

extern "C" {
    PHPCPP_EXPORT void *get_module() {
        static Php::Extension extension("sxg", "1.0");
        Php::Class<Server> obj("Sxg\\Server");
        obj.method<&Server::__construct>("__construct", {
            Php::ByVal("ip", Php::Type::String, true),
            Php::ByVal("port", Php::Type::Numeric, true)
        });
        obj.method<&Server::start>("start");
        obj.method<&Server::on>("on", {
            Php::ByVal("event", Php::Type::String, true),
            Php::ByVal("callback", Php::Type::Callable, true)
        });
        obj.method<&Server::send>("send", {
            Php::ByVal("fd", Php::Type::Numeric, true),
            Php::ByVal("data", Php::Type::String, true)
        });
        obj.method<&Server::close>("close", {
            Php::ByVal("fd", Php::Type::Numeric, true)
        });
        obj.method<&Server::set>("set", {
            Php::ByVal("settings", Php::Type::Array, true)
        });
        extension.add(std::move(obj));
        return extension;
    }
}
//g++ sxg.cpp Client.hpp MainServer.hpp Reactor.hpp NetEvent.hpp -shared -fpic -lpthread -lphpcpp -o sxg.so
//cp sxg.so /usr/local/php8/lib/php/extensions/no-debug-non-zts-20220829/